/*******************************************************************************
版权声明: Copyright(C) HT-Tech Co., Ltd. 2020-2021. All rights reserved.
文件名称: htacl.c
文件描述: ACL访问控制操作
创 建 者: 陈位仅
创建时间: 2024年11月14日
修改历史:
1. 2024年11月14日   陈位仅      创建文件 
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "htmacro.h"
#include "htcfg.h"
#include "htprotocol.h"
#include "htlist.h"

// 添加ACL策略
int htacl_add(char *pIN, int Len)
{
    int rv = 0;
    int index = 0;
    struct list_node *node = NULL; // ACL 节点指针
    in_addr_t addr;                // IP地址
    ht_acl_ip_t *pAcl = NULL;      // ACL 数据结构
    struct in6_addr addr6;         // IPv6地址

    // 创建ACL节点
    node = list_node_creat(pIN, Len);
    if (NULL == node) {
        return -1;
    }

    // ACL 类型解析数据
    pAcl = (ht_acl_ip_t *)pIN;
    switch (pAcl->ht_type)
    {
    case _HT_ACL_TYPE_IPv4:
    case _HT_ACL_TYPE_IPv4_Range:
    case _HT_ACL_TYPE_IPv4_Subnet:
        addr = inet_addr(pAcl->ht_ip);
        index = addr % HT_LIST_MAX_LEN;
        break;
    
    case _HT_ACL_TYPE_IPv6:
    case _HT_ACL_TYPE_IPv6_Range:
    case _HT_ACL_TYPE_IPv6_Subnet:
        rv = inet_pton(AF_INET6, pAcl->ht_ip, &addr6);
        if (rv <= 0) {
            break;
        }
        index = addr6.s6_addr32[3] % HT_LIST_MAX_LEN;
        break;
    default:
        break;
    }

    // 添加到ACL链表
    rv = list_node_add(node, index);
    if (rv < 0) {
        return -1;
    }
    // printf("ACL %d %p %s\n", index, node, (char *)node->data + 1);

    return 0;
}

// IPv4子网访问控制
static int htacl_ipv4_subnet(char *pSubnet, char *ipv4)
{
    int rv = 0;
    int mask = 0;
    int prefix = 0;
    struct in_addr addr;
    struct in_addr subnet;
    ht_acl_subnet_t *pAcl = NULL;  // ACL 数据结构
    int masked_ipv4 = 0;
    int masked_subnet = 0;

    pAcl = (ht_acl_subnet_t *)pSubnet;
    // IPv4地址解析
    rv = inet_pton(AF_INET, ipv4, &addr);
    if (rv <= 0) {
        return -1;
    }

    // 子网解析
    rv = inet_pton(AF_INET, pAcl->ht_sip, &subnet);
    if (rv <= 0) {
        return -1;
    }

    // 计算掩码
    prefix = ntohs(pAcl->ht_preifx);
    mask = htonl((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF);

    // 应用掩码
    masked_ipv4 = ntohl(addr.s_addr) & ntohl(mask);
    masked_subnet = ntohl(subnet.s_addr) & ntohl(mask);

    return masked_ipv4 == masked_subnet;
}

// IPv6子网访问控制
static int htacl_ipv6_subnet(char *pSubnet, char *ipv6)
{
    int i = 0;
    int rv = 0;
    int prefix = 0;
    char mask[16] = {0};           // 子网掩码
    ht_acl_subnet_t *pAcl = NULL;  // ACL 数据结构
    struct in6_addr ipv6_addr;     // IPv6地址
    struct in6_addr ipv6_mask;     // 子网掩码
    struct in6_addr subnet_addr;   // 子网地址

    pAcl = (ht_acl_subnet_t *)pSubnet;
    // IPv6地址解析
    rv = inet_pton(AF_INET6, ipv6, &ipv6_addr);
    if (rv <= 0) {
        return -1;
    }

    // 子网解析
    rv = inet_pton(AF_INET6, pAcl->ht_sip, &subnet_addr);
    if (rv <= 0){
        return -1;
    }

    // 计算掩码
    prefix = ntohs(pAcl->ht_preifx);
    for ( ; i < prefix; i++) {
        mask[i] = 0xff;
    }
    if (prefix % 8 != 0) {
        mask[prefix / 8] = (0xff << (8 - (prefix % 8))) & 0xff;
    }

    // 应用掩码
    for (i = 0; i < 16; i++) {
        ipv6_mask.s6_addr[i] = mask[i] & ipv6_addr.s6_addr[i];
    }

    // 比较前缀
    rv = memcmp(&ipv6_mask, &subnet_addr, 16);

    return rv;
}

// ACL数据比较
static int htacl_cmp(void *data, void *key, int flag)
{
    int rv = 0;
    char *pType = 0;
    ht_acl_ip_t *pIP = NULL;          // ACL 数据结构
    ht_acl_range_t *pRange = NULL;    // ACL 数据结构
    ht_acl_subnet_t *pSubnet = NULL;  // ACL 数据结构

    pType = (char *)data;
    // 数据类型解析
    switch (*pType)
    {
    case _HT_ACL_TYPE_IPv4:
    case _HT_ACL_TYPE_IPv6:
        pIP = (ht_acl_ip_t *)data;
        rv = strcmp(pIP->ht_ip, key);
        break;
    
    case _HT_ACL_TYPE_IPv4_Range:
    case _HT_ACL_TYPE_IPv6_Range:
        pRange = (ht_acl_range_t *)data;
        if (strcmp(pRange->ht_sip, key) < 0
            && strcmp(pRange->ht_eip, key) > 0) {
            rv = 0;
        } else {
            rv = -1;
        }
        break;
    
    case _HT_ACL_TYPE_IPv4_Subnet:
        rv = htacl_ipv4_subnet(data, key);
        break;
    case _HT_ACL_TYPE_IPv6_Subnet:
        rv = htacl_ipv6_subnet(data, key);
        break;

    default:
        break;
    }

    // printf("ACL %s %s %d\n", (char *)data + 1, (char *)key, rv);
    return rv;
}

// ACL策略检测
int htacl_check(int addr, short port)
{
    int index = 0;
    struct list_node *node = NULL;
    struct in_addr addr_ip = {0};

    addr_ip.s_addr = addr;
    for ( ; index < HT_LIST_MAX_LEN; index++) {
        node = list_node_get(index, inet_ntoa(addr_ip), htacl_cmp, 0);
        if (node) {
            return 0;
        }
    }

    return -1;
}
